# StringAppender.java

    package org.codefilarete.tool;

    import java.util.Iterator;

    /**
     * Kind of StringBuilder aimed at being simpler by its API.
     * This class can be inherited and it {@link #cat(Object)} method overridden to append any appropriate representation of its given object, according
     * to its type for example.
     * All other methods refer to it, therefore, by overriding this method, it is guaranteed that the representation will also be applied
     * by other methods.
     *
     * @author Guillaume Mary
     * @see #cat(Object)
     */
    public class StringAppender implements CharSequence {

    	protected StringBuilder appender;

    	public StringAppender() {
    		appender = new StringBuilder();
    	}

    	public StringAppender(int capacity) {
    		appender = new StringBuilder(capacity);
    	}

    	public StringAppender(StringBuilder appender) {
    		this.appender = appender;
    	}

    	public StringAppender(Object... objects) {
    		this();
    		cat(objects);
    	}

    	/**
    	 * Appends given objects to current instance.
    	 * This method is expected o be overridden to let the user append an appropriate representation of the object, typically according to its type.
    	 * All other methods refer to this method, therefore, by overriding this method, it is guaranteed that the representation will also be applied
    	 * by other methods.
    	 *
    	 * @param object object to be appended
    	 * @return this
    	 * @see #ccat(Object...)
    	 */
    	public StringAppender cat(Object object) {
    		appender.append(object);
    		return this;
    	}

    	/**
    	 * Appends given objects to current instance
    	 * @param object1 object to be appended
    	 * @param object2 object to be appended
    	 * @return this
    	 * @see #ccat(Object...)
    	 */
    	public StringAppender cat(Object object1, Object object2) {
    		// we skip an array creation by calling cat() multiple times
    		return cat(object1).cat(object2);
    	}

    	/**
    	 * Appends given objects to current instance
    	 * @param object1 object to be appended
    	 * @param object2 object to be appended
    	 * @param object3 object to be appended
    	 * @return this
    	 * @see #ccat(Object...)
    	 */
    	public StringAppender cat(Object object1, Object object2, Object object3) {
    		// we skip an array creation by calling cat() multiple times
    		return cat(object1).cat(object2).cat(object3);
    	}

    	/**
    	 * Appends given objects to current instance
    	 * @param objects objects to be appended
    	 * @return this
    	 * @see #ccat(Object...)
    	 */
    	public StringAppender cat(Object... objects) {
    		for (Object s : objects) {
    			cat(s);
    		}
    		return this;
    	}

    	/**
    	 * Appends objects present in given {@link Iterable}
    	 * @param objects objects to be appended
    	 * @return this
    	 */
    	public StringAppender cat(Iterable<?> objects) {
    		for (Object s : objects) {
    			cat(s);
    		}
    		return this;
    	}

    	public StringAppender catAt(int index, Object... objects) {
    		// We make this method uses cat() to ensure that any override of cat() is also used by this method
    		// otherwise we could simply use appender.insert(..)
    		// So, we swap this.appender with a new StringBuilder to make cat(..) fill it
    		StringBuilder previous = this.appender;
    		StringBuilder target = new StringBuilder();
    		this.appender = target;
    		// cat() will append to the temporary StringBuilder
    		cat(objects);
    		this.appender = previous;
    		// finally inserting at index
    		this.appender.insert(index, target);
    		return this;
    	}

    	/**
    	 * Appends some object if a condition is met
    	 * @param condition indicates if given objects must be appended
    	 * @param objects objects to be appended
    	 * @return this
    	 */
    	public StringAppender catIf(boolean condition, Object... objects) {
    		if (condition) {
    			cat(objects);
    		}
    		return this;
    	}

    	/**
    	 * Appends given objects, except last one which is used as a separator between them
    	 * @param objects objects to be appended, last one is used as a separator
    	 * @return this
    	 */
    	public StringAppender ccat(Object... objects) {
    		return ccat(objects, objects[objects.length - 1], objects.length - 1);
    	}

    	/**
    	 * Appends objects present in given {@link Iterable}, separated by given separator
    	 * @param objects objects to be appended
    	 * @param sep separator of objects
    	 * @return this
    	 */
    	public StringAppender ccat(Iterable<?> objects, Object sep) {
    		Iterator<?> iterator = objects.iterator();
    		while (iterator.hasNext()) {
    			Object s = iterator.next();
    			cat(s).catIf(iterator.hasNext(), sep);
    		}
    		return this;
    	}

    	/**
    	 * Appends objects present in given array, separated by given separator
    	 * @param objects objects to be appended
    	 * @param sep separator of objects
    	 * @return this
    	 */
    	public StringAppender ccat(Object[] objects, Object sep) {
    		return ccat(objects, sep, objects.length);
    	}

    	/**
    	 * Appends <code>objectCount</code> first objects of given array, separated by given separator
    	 * @param objects objects to be appended
    	 * @param sep separator of objects
    	 * @param objectCount count of object to be taken in input array
    	 * @return this
    	 */
    	public StringAppender ccat(Object[] objects, Object sep, int objectCount) {
    		if (objects.length > 0) {
    			int lastIndex = objectCount < 1 ? 0 : objectCount - 1;
    			for (int i = 0; i < objectCount; i++) {
    				cat(objects[i]).catIf(i != lastIndex, sep);
    			}
    		}
    		return this;
    	}

    	/**
    	 * Appends some objects at the beginning and end of current instance
    	 * @param open the object to be added at very first place of current instance
    	 * @param close the object to be appended at the end of current instance
    	 * @return this
    	 */
    	public StringAppender wrap(Object open, Object close) {
    		catAt(0, open).cat(close);
    		return this;
    	}

    	/**
    	 * Implemented to print the underlying char sequence
    	 * @return a {@link String} representation of all appended objects
    	 */
    	@Override
    	public String toString() {
    		return appender.toString();
    	}

    	/**
    	 * Removes some last characters of current instance.
    	 * @param nbChar count of character to be removed
    	 * @return this
    	 */
    	public StringAppender cutTail(int nbChar) {
    		int newLength = length() - nbChar;
    		if (newLength > -1) {
    			appender.setLength(newLength);
    		}
    		return this;
    	}

    	/**
    	 * Removes some first characters of current instance.
    	 * @param nbChar count of character to be removed
    	 * @return this
    	 */
    	public StringAppender cutHead(int nbChar) {
    		appender.delete(0, nbChar);
    		return this;
    	}

    	/**
    	 * Gives access to delegate appender, because it can be useful to append directly to the standard API StringBuilder
    	 *
    	 * @return the underlying appender
    	 */
    	public StringBuilder getAppender() {
    		return appender;
    	}

    	@Override
    	public int length() {
    		return appender.length();
    	}

    	@Override
    	public char charAt(int index) {
    		return appender.charAt(index);
    	}

    	@Override
    	public CharSequence subSequence(int start, int end) {
    		return appender.subSequence(start, end);
    	}

    }